import keysTextJson from '../json/keystext.json';
import querySymbol from '../json/querySymbol.json';
import queryGid from '../json/queryGid.json';

/* 公式的正则对象 */
const regex = [
        {
            val: '\\S*\\frac {\\S*} {\\S*}',
            reg: /\d*?\\frac\s*{.*?}\s*{.*?}/,
        },
        {
            val: '\\sqrt[{2}] {\\S*}',
            reg: /\\sqrt\[2\]\s*{.*?}|\\sqrt\s*{.*?}/,
        },
        {
            val: '\\sqrt[{3}] {\\S*}',
            reg: /\\sqrt\[3\]\s*{.*?}/,
        },
        {
            val: '{\\S*}^{\\S*}',
            reg: /{.*?}\^{.*?}/,
        },
        {
            val: '{\\S*}_{\\S*}',
            reg: /{.*?}_{.*?}/,
        },
        {
            val: '\\dot {\\S*}',
            reg: /\\dot\s*{.*?}/,
        },
    ],
    regexSyllable = /^[a-uw-zāáǎàōóǒòēéěèīíǐìūúǔùüǖǘǚǜ]+$/; // 拼音

/**
 * 判断参数是否为数字
 * @param val
 * @returns {boolean} true === 数字
 * @private
 */
export const _onlyNumber = (val) => {
    return /^\+?-?\d+(\.\d*)?$/.test(val);
};

/**
 * 识别是否只含有公式和数字和数字键盘包含的符号
 * @param arr
 * @returns {*}
 * @private
 */
export const _onlyFormula = (arr) => {
    return arr.length > 0 && arr.every(itm => regex.some(({ val: sym, reg }) => {
        let res = (itm + '').match(reg);
        if (res) {
            let f = res.length === 1 && res[0] === itm; // 只有公式
            let ipt = itm.match(/{.*?}\^|{.*?}_|{.*?}|\[.*?\]/igm);
            let g = ipt && ipt.every(iptItm => {
                iptItm = (iptItm.match(/{.*}|\[.*\]/igm) || [''])[0];
                return _onlyNumber(iptItm.slice(1, -1));
            }); // 内容只有数字
            return f && g;
        }
        return 0;
    }) || _onlyNumber(itm) || _inNumSymble(itm));
};

/*
* 拼音键盘
* */
const _matchSyllable = p => {
    return p.length > 0 && p.every(item => regexSyllable.test(item + ''));
};

/**
 * 匹配键盘类型
 * @param arr
 * @returns {{calcType: string}}
 * @private
 */
const _matchType = (arr) => {
    let val = ''; let calcType = null;
    arr.forEach(v => { val += v + ' '; });
    val = val.trim();
    let hasChinese = val.match(/[\u4E00-\u9FA5]+/gm); // 有汉字
    let hasNum = val.match(/\d/gm); // 有数字
    let hasEng = val.match(/[a-z]/igm); // 有字母
    let hasNumSymble = val.match(/[+>＞\-<＜×=÷π%.°:……]/igm); // 有数学符号
    // debugger;
    // 只要答案里面有超过数字键盘范围的答案  不调用数字键盘
    if (hasChinese) {
        if (hasNum || hasNumSymble) {
            calcType = 'combo';
        } else {
            calcType = 'chinese';
        }
    } else if (hasNum || hasNumSymble) {
    /* 含有数字且没有汉字，使用数字键盘 */
        calcType = 'number';
        if (hasEng) {
            /* 含有数字和子母，使用通用键盘 */
            calcType = 'combo';
        }
    } else if (hasEng) {
    /* 含有字母且没有汉字和数字，使用子母键盘 */
        calcType = 'letters';
    } else {
        calcType = 'symbol';
    }
    return {
        calcType: calcType,
    };
};

/**
 * 组合键盘时，根据首个答案匹配优先显示的键盘类型
 * @param first
 * @param prev
 * @returns {*}
 * @private
 */
const _matchModel = (first, prev, symbols) => {
    if (first !== undefined) {
        let f = first.match(/\\frac\s*{.*}\s*{.*}/); // 含有分数
        let onlyF = f && f.length === 1 && f[0] === first; // 只含有分数
        let onlySym = !!symbols.slice(1).find(itm => itm.text === first); // 只含有符号
        let onlyN = (first.match(/{.*?}/igm) || []).every(val => _onlyNumber(val.slice(1, -1))); // 只含有数字
        if ((onlyF && onlyN) || _onlyNumber(first)) {
            return {
                matchBoardType: 'comboNumber',
                prevBoardType: prev,
            };
        } else if (onlySym) {
            return {
                matchBoardType: 'symbol',
                prevBoardType: prev,
            };
        }
    }
    return {};
};

/* 格式化view的unicode码 */
Object.entries(querySymbol).forEach(([k, obj]) => {
    Object.entries(obj).forEach(([p, val]) => {
        let view = val.view;
        let type = val.type || 'symbol';
        let cls = 'bold';
        if (view && view.startsWith('&#')) {
            if (type.includes('slim')) cls = '';
            Object.assign(val, {
                view: fromUnicode(val.view),
                type: type + ' ' + cls,
            });
        }
    });
});

/**
 * 计算键盘类型的入口
 * 这个非常的重要，根据答案、学科识别入口显示哪种键盘
 * @param rightAnswers
 * @param subject
 * @param boardType
 * @private
 */
export const _computeType = ({ rightAnswers, lastBoardType, boardType, subject }) => {
    let calcType = boardType === 'none' ? (lastBoardType.boardType || 'public') : boardType;
    if (['audio', 'choosePhoto'].includes(calcType)) {
        calcType = 'public';
    }
    if (boardType === 'none') {
        if (_onlyFormula(rightAnswers)) {
            calcType = 'number'; // 数字键盘
        } else if (_matchSyllable(rightAnswers) && subject + '' === '2') {
            calcType = 'syllable'; // 拼音键盘
        }
    }
    let prevBoardType = [];
    let fullFirst = true;
    // 默认显示所有符号
    let symbols = [{ 'name': '常用' }, { 'name': '数学' }, { 'name': '中文' }, { 'name': '英文' }, { 'name': '拼音' }, { 'name': '偏旁' }, { 'name': '部首' }, { 'name': '笔画' }, { 'name': '序号' }, { 'name': '图形' }, { 'name': '音标' }, { 'name': '注音' }, { 'name': '物理' }, { 'name': '化学' }, { 'name': '箭头' }];
    let itmesarr = [];
    Object.entries(queryGid).forEach(([g, obj]) => {
        itmesarr[obj.name] = [];
        Object.entries(obj.group).forEach(([k, val]) => {
            let tmpitem = { 'text': k };
            if (val.type) {
                tmpitem.type = val.type;
            } else {
                tmpitem.type = 'symbol';
            }

            if (val.view) {
                tmpitem.view = fromUnicode(val.view);
            }

            itmesarr[obj.name].push(tmpitem);
        });
    });
    for (var i = 0; i < symbols.length; i++) {
        symbols[i].items = itmesarr[symbols[i].name];
    }
    return {
        symbols, // 符号
        prevBoardType, // 前一个键盘类型
        calcType, // 当前键盘类型
        fullFirst,
    };
};

export const _defSymbols = (boardType) => {
    return Object.entries((keysTextJson[boardType === 'none' && 'combo' || boardType] || { data: {} }).data.defaultSymbol || {}).map(([k, val]) => {
        return Object.assign({}, val, { text: k });
    });
};

export const browerEnv = () => {
    let u = navigator.userAgent;
    return {
    // 判断是否IE内核
        trident: u.indexOf('Trident') > -1,
        // 判断是否opera内核
        presto: u.indexOf('Presto') > -1,
        // 判断是webKit苹果谷歌内核
        webKit: u.indexOf('AppleWebKit') > -1,
        // 判断是否火狐内核
        gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') === -1,

        // 判断浏览器类型
        firefox: u.indexOf('Firefox') > -1,
        chrome: u.indexOf('Chrome') > -1,
        safari: !u.indexOf('Chrome') > -1 && u.indexOf('Safari') > -1,
        opera: u.indexOf('Opera') > -1,
        ie: u.indexOf('MSIE') > -1,
        // 判断是否为移动终端
        mobile: !!u.match(/AppleWebKit.*Mobile.*/),
        // 判断是否为Windows平台
        windows: u.indexOf('Windows'),

        // 判断是否ios终端
        ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/),
        // 判断是否android终端
        android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1,
        // 判断是否为iPhone终端
        iPhone: u.indexOf('iPhone') > -1,
        // 判断是否iPad
        iPad: u.indexOf('iPad') > -1,
        // 判断是否iPod
        iPod: u.indexOf('iPod') > -1,
        // 是否web应用程序
        webApp: u.indexOf('Safari') === -1,

        // 判断是否微信浏览器
        weixin: u.indexOf('MicroMessenger') > -1,
        // 判断是否为QQ浏览器
        qq: u.match(/\sQQ/i) === ' qq', // 是否QQ
    };
};
// 原生xhr简单封装
export const ajax = ({ type, url, data, isAsync, success }) => {
    if (!url) {
        console.error('请输入请求地址');
        return;
    }

    const xhr = new XMLHttpRequest();

    // 处理data对象
    let query = []; let queryData;
    for (let key in data) {
    // 默认encodeURIComponent一下
        query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
    }
    queryData = query.join('&');

    if (type === 'GET') {
    // get方式参数要跟在url上
        url = url + '?' + queryData;
    }

    // 默认使用GET,默认异步
    xhr.open(type || 'GET', url, isAsync || true);
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            // 有传入success回调就执行
            success && success(JSON.parse(xhr.responseText));
        }
    };

    if (type === 'POST') {
    // 给指定的HTTP请求头赋值
        xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        // 数组转成字符串
        xhr.send(data);
    } else {
        xhr.send();
    }
};

/**
 * 匹配符号
 * @param calcType
 * @param rightAnswers
 * @param gId
 */
function matchSymbol (rightAnswers, gId, cls, { name, group }) {
    // 匹配公式，若只含有公式，结束匹配
    if (matchFormula(rightAnswers, gId)) return;
    /* 识别以空格区分的符号 */
    rightAnswers.split(' ').forEach(val => {
        let m = group[val];
        if (m) putGid(gId, m.gid, val, cls, name);
    });
    /* 截取英文组成的符号 */
    let y = rightAnswers.match(/[a-z]+/igm) || [];
    y.forEach(val => {
        let m = group[val];
        /* // 获取英文组成的符号的组id */
        if (m) putGid(gId, m.gid, val, cls, name);
    });
    /* 截取空格隔开的英文组成的符号 */
    let lsl = rightAnswers.match(/\w+ \w+/igm) || [];
    lsl.forEach(val => {
        let m = group[val];
        /* // 获取英文组成的符号的组id */
        if (m) putGid(gId, m.gid, val, cls, name);
    });
    /* 获取普通符号的组id */
    for (let i of rightAnswers) {
        let m = group[i];
        if (m) putGid(gId, m.gid, i, cls, name);
    }
    /* 整个字符作为符号时 */
    let match = group[rightAnswers];
    if (match) putGid(gId, match.gid, rightAnswers, cls, name);

    /* 以符号组匹配字符, 但不能匹配出公式 */
    Object.entries(group).forEach(([k, { gid: id }]) => {
        rightAnswers.includes(k) && putGid(gId, id, k, cls, name);
    });
}

function putGid (obj, id, k, cls, name) {
    if (Array.isArray(id)) {
        id.forEach(itm => {
            match(obj, itm, k, cls, name);
        });
    } else {
        match(obj, id, k, cls, name);
    }
}

function match (obj, id, k, cls, name) {
    if (!obj[cls]) {
        obj[cls] = {
            name,
            group: {
                [id]: {
                    [k]: {},
                },
            },
        };
    } else {
        if (obj[cls].group[id]) {
            obj[cls].group[id][k] = {};
        } else {
            obj[cls].group[id] = { [k]: {} };
        }
    }
}

/* 判断默认符号是够包含必备符号 */
function includeRequire (souce, target) {
    return !Object.entries(souce).some(([k]) => !target[k]);
}

/**
 * 防重复执行
 * @param fn 应执行的函数
 * @param wait 距离最后一次触发的时间
 * @param outset 是否是开始时执行
 *    true => 开始时间点执行
 *    false => 结束时间点执行
 *    'fixed' => 固定间隔wait时间段后，能触发一次
 *  @param 'forwards' => 最后一次触发是否响应
 */
export const $debounce = (fn, wait, outset = false, forwards = false) => {
    let context = null;
    let lastCall = null;
    let argu = null;
    let timeOut = null;
    let isFix = outset === 'fixed';

    /* 延迟检测函数，检测距离最后一次触发的时间间隔 */
    function timeUp () {
        let interval = new Date() - lastCall; // 最后一次触发的时间间隔;
        if (interval < wait) {
            timeOut = setTimeout(timeUp, isFix && wait || wait - interval);
            /* fixed模式的固定时间点执行 */
            isFix && fn.apply(context, argu);
        } else {
            timeOut = null;
            if (!outset || (isFix && forwards)) { // 结束时间点执行和fixed模式的最后一次触发时间点执行
                fn.apply(context, argu);
            }
            argu = context = null;
        }
    }

    /* 返回闭包函数 */
    return function (...rest) {
        context = this;
        argu = rest;
        /* 记录最后触发时间 */
        if (!isFix || forwards || !lastCall) {
            lastCall = new Date();
        }
        if (outset && !timeOut) { // 开始时间点执行和fixed模式的固定时间点执行
            fn.apply(context, argu);
        }
        if (!timeOut) timeOut = setTimeout(timeUp, wait); // 首次触发产生延迟函数
    };
};

/**
 * 匹配公式
 */
function matchFormula (answer, obj) {
    return regex.some(({ reg, val }) => {
        let res = answer.match(reg);
        res && putGid(obj, 'G_2_25', val, 'G_2_', '数学');
        return answer === (res || [''])[0];
    });
}

/**
 * 嵌入符号到各键盘的符号位置
 */
export function _implant (src, rules, defSym) {
    let res = [{
        text: '符号',
        type: 'symbol middle',
        expect: 'switch',
    }];
    let start = {};
    let def = {};
    rules.forEach(({ index, count }) => {
        let result = takeOut(src[index], count, (start[index] || 0));
        let resArr = result.res;
        Object.assign(start, { [index]: result.start });
        Array.from({ length: count - resArr.length }).forEach(() => {
            let { itm, idx: i } = compare(defSym, resArr, (def[index] || 0));
            def[index] = i;
            resArr.push(itm);
        });
        res.push(...resArr);
    });
    return res;
}

/**
 * 从不同符号数组中取出符号，再重组
 */
function takeOut (arr, count, start = 0, res = [], idxMap = []) {
    let ele = arr[start];
    if (ele) {
        if (ele.type.includes('double')) {
            start = takeOut(arr, count, ++start, res).start;
        } else {
            res.push(ele);
            idxMap.push(start);
            count--;
            start++;
            if (count > 0) {
                start = takeOut(arr, count, start, res).start;
            }
        }
    }
    return {
        start,
        res,
        idxMap,
    };
}

/**
 * 去除重复的符号
 */
function compare (origin, comp, idx) {
    let itm = origin[idx];
    idx++;
    if (comp.find(ele => ele.text === itm.text)) {
        ({ itm, idx } = compare(origin, comp, idx));
    }
    return {
        itm, idx,
    };
}

/***
 * 根据学科优先级取符号
 * @param rightAnswers
 * @param subject
 * @param gId
 * @returns {*}
 */
function getGid (rightAnswers, gId = {}, first) {
    /* 获取gid */
    rightAnswers.forEach(val => {
    /* 获取符号组id */
        Object.entries(queryGid).forEach(([g, obj]) => {
            matchSymbol(val, gId, g, obj);
        });
    });
    return { gId };
}

/**
 * unicode to str
 * @param str
 * @returns {*}
 */
export function fromUnicode (str) {
    return str.replace(/&#((x[0-9A-Fa-f]+)|([0-9]+));/g, word => String.fromCharCode('0' + word.split('#')[1].split(';')[0]));
}

/**
 *深拷贝
 * @peram deep  true => 深拷贝, vue => 适用于VUE的深拷贝(删除原对象多余的数组),
 *              'add' => 只增加新属性的深拷贝, 'reader' => 只改变已有属性的深拷贝
 *              被拷贝属性为 undefined时跳过
 * @return 目标对象
 */

export function $extend (deep, ...rest) {
    if (deep !== true && typeof deep === 'object') { // simple copy
        return Object.assign(deep, ...rest);
    } else if (rest.every(v => typeof v === 'object')) { // deep copy
        let [own, ...copy] = rest;
        deep === 'add' && copy.reverse();
        copy.forEach(obj => {
            let isArray = Object.prototype.toString.call(obj) === '[object Array]' && !!own.splice;
            if (deep === 'vue' && isArray && obj.length < 1) own.splice(0, own.length);
            for (let key in obj) {
                if (!obj.hasOwnProperty(key)) continue;// ignore prototype
                let ownVal = own[key]; let copyVal = obj[key];
                let hasProperty = own.hasOwnProperty(key);
                let isLimit = !(deep === 'add' && hasProperty) && !(deep === 'reader' && !hasProperty);
                if (typeof copyVal === 'object' && copyVal !== null) { // Copy the arrays, objects
                    let constCopy = copyVal.constructor;
                    let constOwn = (ownVal !== undefined && ownVal !== null) && ownVal.constructor;
                    if (constOwn === constCopy) {
                        $extend(deep, ownVal, copyVal);
                        if (isArray) {
                            if (deep === 'vue' && key * 1 === obj.length - 1) {
                                own.splice(obj.length);
                            }
                        }
                    } else if (isLimit) { // ownVal === undefined or null
                        if (isArray) {
                            own.splice(key, 1, $extend(deep === 'reader' && true || deep, constCopy === Array ? [] : {}, copyVal));
                            if (deep === 'vue' && key * 1 === obj.length - 1) {
                                own.splice(obj.length);
                                break;
                            }
                        } else {
                            $extend(deep === 'reader' && true || deep, own[key] = constCopy === Array ? [] : {}, copyVal);
                        }
                    }
                } else if (isLimit && copyVal !== undefined) {
                    if (isArray) {
                        own.splice(key, 1, copyVal);
                        if (deep === 'vue' && key * 1 === obj.length - 1) {
                            own.splice(obj.length);
                        }
                    } else {
                        own[key] = copyVal;
                    }
                }
            }
        });
        return own;
    }
}

/**
 * 判断参数是否属于数字键盘的符号
 * @param val
 * @returns {boolean} true === 数字
 * @private
 */
export const _inNumSymble = (val) => {
    val += '';
    let reg = val.match(/[+\-×÷=>＞<＜.%π°:……\d]/igm);
    return reg && reg.length === val.length; // 有数学符号
};
